﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace Face_Recognition.Haar
{
    public class Detector
    {
        private List<System.Drawing.Rectangle> detectedObjects;
        private Classifier classifier;

        private Size minSize = new Size(15, 15);
        private Size maxSize = new Size(500, 500);
        private float factor = 1.2f;

        private System.Drawing.Rectangle[] lastObjects;
        private int steadyThreshold = 2;

        private int baseWidth;
        private int baseHeight;

        private int lastWidth;
        private int lastHeight;
        private float[] steps;

        public int Steady { get; private set; }

        public float ScalingFactor
        {
            get { return factor; }
            set
            {
                if (value != factor)
                {
                    factor = value;
                    steps = null;
                }
            }
        }

          public Detector(Cascade cascade)
            : this(cascade, 15) { }

           public Detector(Cascade cascade, int minSize)
            : this(cascade, minSize, 1.2f) { }

        public Detector(Cascade cascade, int minSize, float scaleFactor)
        {
            this.classifier = new Classifier(cascade);
            this.minSize = new Size(minSize, minSize);
            this.factor = scaleFactor;
            this.detectedObjects = new List<System.Drawing.Rectangle>();

            this.baseWidth = cascade.InitialWidth;
            this.baseHeight = cascade.InitialHeight;
        }

        public System.Drawing.Rectangle[] ProcessFrame(IntegralImage integralImage)
        {
            this.detectedObjects.Clear();

            int width = integralImage.Width;
            int height = integralImage.Height;

            if (steps == null || width != lastWidth || height != lastHeight)
                update(width, height);


            System.Drawing.Rectangle window = System.Drawing.Rectangle.Empty;

            for (int i = 0; i < steps.Length; i++)
            {
                float currentScale = steps[i];

                classifier.Scale = currentScale;

                window.Width = (int)(baseWidth * currentScale);
                window.Height = (int)(baseHeight * currentScale);

                int xStep = window.Width >> 3;
                int yStep = window.Height >> 3;

                int xEnd = width - window.Width;
                int yEnd = height - window.Height;

                
                    for (int y = 0; y < yEnd; y += yStep)
                    {
                        window.Y = y;

                        for (int x = 0; x < xEnd; x += xStep)
                        {
                            window.X = x;

                            if (overlaps(window))
                                continue; 

                            if (classifier.Compute(integralImage, window))
                            {
                                detectedObjects.Add(window);
                            }
                        }
                    }
            }

            System.Drawing.Rectangle[] faces = detectedObjects.ToArray();

            checkSteadiness(faces);
            lastObjects = faces;

            return this.ColorCheck(faces, integralImage.Bitmap);
        }

        private void update(int width, int height)
        {
            List<float> listSteps = new List<float>();

                float start = 1f;
                float stop = Math.Min(width / (float)baseWidth, height / (float)baseHeight);
                float step = factor;

                for (float f = start; f < stop; f *= step)
                    listSteps.Add(f);

            steps = listSteps.ToArray();

            lastWidth = width;
            lastHeight = height;
        }

        private void checkSteadiness(System.Drawing.Rectangle[] rectangles)
        {
            if (lastObjects == null ||
                rectangles == null ||
                rectangles.Length == 0)
            {
                Steady = 0;
                return;
            }

            bool equals = true;
            foreach (System.Drawing.Rectangle current in rectangles)
            {
                bool found = false;
                foreach (System.Drawing.Rectangle last in lastObjects)
                {
                    if (current.IsRectangleEqual(last, steadyThreshold))
                    {
                        found = true;
                        continue;
                    }
                }

                if (!found)
                {
                    equals = false;
                    break;
                }
            }

            if (equals)
                Steady++;

            else
                Steady = 0;
        }

        private System.Drawing.Rectangle[] ColorCheck(System.Drawing.Rectangle[] faces, Bitmap bitmap) {

            List<System.Drawing.Rectangle> facesList = faces.ToList();

            for (int i = 0; i < faces.Length; i++)
            {
                if (!ColorFilters.CheckRectangleColors(bitmap, faces[i]))
                    facesList.Remove(faces[i]);
            }

            return facesList.ToArray();
        }

        private bool overlaps(System.Drawing.Rectangle rect)
        {
            foreach (System.Drawing.Rectangle r in detectedObjects)
            {
                if (rect.IntersectsWith(r))
                    return true;
            }
            return false;
        }

    }
}
